import sys

from PySide6.QtWidgets import QWidget, QVBoxLayout, QPlainTextEdit, QLabel, QCheckBox, QLineEdit, QPushButton, QFileDialog,\
                                QHBoxLayout, QApplication, QMessageBox, QLayout, QDialog
from PySide6.QtCore import Qt, Signal, QThread, Slot

from openpyxl import load_workbook
import json
from pathlib import Path

from api import api_convert, api_ui


class StatementUi(QDialog):
    """StatementUi
        月报/季报/年报的调整分录汇总表的GUI
    """
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)
    send_to_subthread_signal = Signal(bool)
    log_signal = Signal(str, str)

    def __init__(self, group_box_style: str):
        super().__init__()
        self.setWindowTitle('调整分录汇总表')
        # self.resize(600, 480)
        self.adjustSize()
        self.main_ui()
        
    def check_input(self):
        if self.yuan_checkbox.isChecked() or self.wanyuan_checkbox.isChecked():
            if self.balance_path_edit.text() and self.statement_path_edit.text():
                self.run_thread()
            else:
                self.show_msg_signal({'message': '请选择文件路径'})
        else:
            self.show_msg_signal({'message': '请选择单位'})

    def run_thread(self):
        if hasattr(sys, '_MEIPASS'):
            current_dir = Path(sys._MEIPASS)
        else:
            current_dir = Path(__file__).resolve().parent
        rule_path = current_dir / 'rule.json'

        self.start_btn.setEnabled(False)
        self.start_btn.setText('执行中')
        self.rel_tip_signal.emit('开始', 'dark')

        unit = '元' if self.yuan_checkbox.isChecked() else '万元'
        balance_path = self.balance_path_edit.text()
        statement_path = self.statement_path_edit.text()

        if self.correct_checkbox.isChecked():
            self.thread = CorrectThread(balance_path, statement_path)
            self.thread.rel_tip_signal.connect(self.rel_tip_signal)
            self.thread.finished_signal.connect(self.after_finish)
            self.thread.show_msg_signal.connect(self.show_msg_signal)
            self.thread.start()
        else:
            self.thread = StatementThread(unit, rule_path, balance_path, statement_path)
            self.thread.rel_tip_signal.connect(self.rel_tip_signal)
            self.thread.finished_signal.connect(self.after_finish)
            self.thread.show_msg_signal.connect(self.show_msg_signal)
            self.send_to_subthread_signal.connect(self.thread.receive_decision)         # 将用户选择发送回线程
            self.thread.start()

    def main_ui(self):
        # 提示区域
        # 正常模式
        self.normal_mode_tip_widget = QWidget()
        # 设置背景颜色为浅黄色
        self.normal_mode_tip_widget.setStyleSheet('background-color: #F5F5DC;')
        self.normal_mode_tip_layout = QVBoxLayout(self.normal_mode_tip_widget)
        first_tip_checkbox = QCheckBox('科目余额表展开二级科目：其他应付款单位余额表、坏账准备、合同资产减值准备、合同结算、财务费用（、应交税费）')
        second_tip_checkbox = QCheckBox('手填：其他流动资产、其他流动负债')
        third_tip_checkbox = QCheckBox('检查：合同资产和合同负债（如存在已计量未完工）')
        fourth_tip_checkbox = QCheckBox('导入财务云报表，配平后导出')
        self.normal_mode_tip_layout.addWidget(QLabel('执行前：'))
        self.normal_mode_tip_layout.addWidget(first_tip_checkbox)
        self.normal_mode_tip_layout.addWidget(QLabel('执行后：'))
        self.normal_mode_tip_layout.addWidget(second_tip_checkbox)
        self.normal_mode_tip_layout.addWidget(third_tip_checkbox)
        self.normal_mode_tip_layout.addWidget(fourth_tip_checkbox)
        # self.normal_mode_tip_layout.addWidget(fifth_tip_checkbox)
        # 修改模式
        self.correct_mode_tip_widget = QWidget()
        self.correct_mode_tip_widget.setStyleSheet('background-color: #F5F5DC;')
        self.correct_mode_tip_widget.hide()
        self.correct_mode_tip_layout = QVBoxLayout(self.correct_mode_tip_widget)
        tip_checkbox1 = QCheckBox('先将调整分录表中的调整数据全部删除')
        tip_checkbox2 = QCheckBox('导出调整分录汇总表')
        self.correct_mode_tip_layout.addWidget(tip_checkbox1)
        self.correct_mode_tip_layout.addWidget(tip_checkbox2)

        self.tip_edit = QPlainTextEdit()
        self.tip_edit.setReadOnly(True)
        self.tip_edit.setPlainText('''3.执行修改模式时，先将调整分录表中的调整数据全部删除，再导出调整分录汇总表''')
        # self.tip_edit.setStyleSheet('background-color: #F0F0F0;')
        self.tip_edit.setFixedHeight(100)

        # 设置单位，元/万元
        self.unit_widget = QWidget()        # 为了实现在修改模式中隐藏的效果，需要将unit区域放入一个widget
        self.unit_layout = QHBoxLayout(self.unit_widget)
        self.unit_label = QLabel('单位')
        self.unit_label.setFixedWidth(90)
        self.unit_label.setAlignment(Qt.AlignCenter)
        self.unit_layout.addWidget(self.unit_label)
        self.yuan_checkbox = QCheckBox('元')
        self.wanyuan_checkbox = QCheckBox('万元')
        self.wanyuan_checkbox.setChecked(True)
        self.yuan_checkbox.stateChanged.connect(lambda x: self.checkbox_clicked(x, self.wanyuan_checkbox))
        self.wanyuan_checkbox.stateChanged.connect(lambda x: self.checkbox_clicked(x, self.yuan_checkbox))
        self.unit_layout.addWidget(self.yuan_checkbox)
        self.unit_layout.addWidget(self.wanyuan_checkbox)

        # 选择科目余额表的路径
        self.balance_layout, self.balance_label, self.balance_path_edit, self.balance_button = api_ui.file_path_ui('科目余额表', False)
        
        # 选择调整分录汇总报的路径
        self.statement_layout, self.statement_label, self.statement_path_edit, self.statement_button = api_ui.file_path_ui('调整分录汇总表', False)

        # 修改模式
        self.correct_checkbox = QCheckBox('修改模式')
        self.correct_checkbox.clicked.connect(self.correct_mode)
        self.correct_checkbox.setToolTip('根据正确调整分录汇总表，修改错误汇总表')

        # 开始按钮
        self.start_btn = QPushButton('开始')
        self.start_btn.setFixedWidth(60)
        self.start_btn.clicked.connect(self.check_input)

        # 宏观布局
        main_layout = QVBoxLayout()
        main_layout.setAlignment(Qt.AlignCenter)
        main_layout.setSizeConstraint(QLayout.SetNoConstraint)
        self.setLayout(main_layout)
        main_layout.addWidget(self.normal_mode_tip_widget)
        main_layout.addWidget(self.correct_mode_tip_widget)
        main_layout.addWidget(self.unit_widget)
        main_layout.addLayout(self.balance_layout)
        main_layout.addLayout(self.statement_layout)
        main_layout.addWidget(self.correct_checkbox, alignment=Qt.AlignRight)
        main_layout.addWidget(self.start_btn, alignment=Qt.AlignCenter)

    @Slot(bool, QCheckBox)
    def checkbox_clicked(self, state, exclusive_checkbox):
        # 实现复选框的互斥效果
        if state:
            exclusive_checkbox.setChecked(False)
        else:
            exclusive_checkbox.setChecked(True)

    @Slot()
    def after_finish(self, state):
        self.start_btn.setText('开始')
        self.start_btn.setEnabled(True)
        if state:
            self.rel_tip_signal.emit('完成', 'green')
        else:
            self.rel_tip_signal.emit('失败，提前退出', 'red')
    
    @Slot()
    def correct_mode(self):
        if self.correct_checkbox.isChecked():
            self.normal_mode_tip_widget.hide()
            self.correct_mode_tip_widget.show()
            self.unit_widget.hide()
            self.balance_label.setText('正确汇总表')
            self.balance_path_edit.setText('')
            self.balance_path_edit.setPlaceholderText('请选择正确汇总表')
            self.statement_label.setText('错误汇总表')
            self.statement_path_edit.setText('')
            self.statement_path_edit.setPlaceholderText('请选择错误汇总表')
        else:
            self.normal_mode_tip_widget.show()
            self.correct_mode_tip_widget.hide()
            self.unit_widget.show()
            self.balance_label.setText('科目余额表')
            self.balance_path_edit.setPlaceholderText('请选择科目余额表')
            self.statement_label.setText('调整分录汇总表')
            self.statement_path_edit.setPlaceholderText('请选择调整分录汇总表')

    @Slot(bool)
    def receive_decision(self, choice):
        """槽函数，用于接收用户选择，并发送给子线程"""
        self.send_to_subthread_signal.emit(choice)
        
class StatementThread(QThread):
    rel_tip_signal = Signal(str, str)
    finished_signal = Signal(int)             # 正常结束发1，提前结束发0
    show_msg_signal = Signal(dict)

    def __init__(self, unit, rule_path, balance_path, statement_path):
        super().__init__()
        self.unit = unit
        self.rule_path = rule_path
        self.balance_path = balance_path
        self.statement_path = statement_path
        self.user_choice = None  # 存储用户的选择

    def wait_for_decision(self):
        self.user_choice = None  # 重置用户选择
        while self.user_choice is None:  # 等待用户选择完成
            self.msleep(100)

    @Slot(bool)
    def receive_decision(self, choice):
        """槽函数，用于接收用户选择"""
        self.user_choice = choice

    def get_value(self, i, the_cell):
        value = the_cell.value
        if value is None:
            num = self.numbers[i]
            name = self.names[i]
            message = f"科目余额表 {the_cell.coordinate} 单元格为空，对应的A,B列为 {num} {name}，是否继续？"
            self.show_msg_signal.emit({'message': message, 'set_btn': True})
            return
        if isinstance(value, float) or isinstance(value, int):
            return value
        elif isinstance(value, str):
            try:
                return float(value.replace(',', ''))
            except ValueError:
                self.show_msg_signal.emit({'message': f"科目余额表 {the_cell.coordinate} 单元格为非数值类型"})

    def other_receivables_payables(self, source_data, d=10000):
        _other_receivables = source_data['1221']['H']['account']
        _other_payables_qd = source_data['Z01140560000000000']['H']['account']
        _other_payables_others = source_data['2241']['H']['account'] - _other_payables_qd
        if _other_receivables > _other_payables_qd:
            other_receivables = _other_receivables - _other_payables_qd
            other_payables = _other_payables_others
        else:
            other_receivables = 0
            other_payables = _other_payables_others + _other_payables_qd - _other_receivables
        decimal_palces = 2 if d == 1 else 1
        return {'C20': api_convert.convert_to_wan(other_receivables, divisor=d, decimal_places=decimal_palces), 'C74': api_convert.convert_to_wan(other_payables, divisor=d, decimal_places=decimal_palces)}
            
    def run(self):
        wb_source = load_workbook(self.balance_path, data_only=True)
        ws_source = wb_source.active
        self.numbers = [c.value for c in ws_source['A'][5:]]
        self.names = [c.value for c in ws_source['B'][5:]]
        f_acount = []
        for i, the_cell in enumerate(ws_source['F'][5:]):
            cell_value = self.get_value(i, the_cell)
            if cell_value is None:
                self.wait_for_decision()  # 等待用户选择
                if not self.user_choice:
                    self.rel_tip_signal.emit('程序提前结束', 'red')
                    self.finished_signal.emit(0)
                    return
                else:
                    self.rel_tip_signal.emit(f'忽略空单元格 {the_cell.coordinate}，继续执行', 'yellow')
            f_acount.append(cell_value)

        symbol = [c.value for c in ws_source['G'][5:]]
        h_acount = []
        for i, the_cell in enumerate(ws_source['H'][5:]):
            cell_value = self.get_value(i, the_cell)
            if cell_value is None:
                self.wait_for_decision()  # 等待用户选择
                if not self.user_choice:
                    self.rel_tip_signal.emit('程序提前结束', 'red')
                    self.finished_signal.emit(0)
                    return
                else:
                    self.rel_tip_signal.emit(f'忽略空单元格 {the_cell.coordinate}，继续执行', 'yellow')
            h_acount.append(cell_value)

        source_data = {}    # 科目余额表中的数据
        for i, num in enumerate(self.numbers):
            source_data[num] = {'F': {'symbol': '贷', 'account': f_acount[i]}, 'H': {'symbol': symbol[i], 'account': h_acount[i]}}

        with open(self.rule_path, 'r', encoding='utf-8') as f:
            rules = json.load(f)
        wb_statement = load_workbook(self.statement_path)
        ws_statement  = wb_statement.active
        # 删除 C5:C171 的数据
        for i in range(5, 171):
            ws_statement[f'C{i}'] = ''

        results = {}
        for i, num in enumerate(self.numbers):
            try:
                info_rule = rules[num]
            except KeyError:
                continue
            # 检查余额方向
            direction = info_rule['direction_check']
            if direction:
                if source_data[num]['H']['symbol'] not in {direction, '平'}:
                    self.rel_tip_signal.emit(f'计算 {self.names[i]} 时，科目 {num} 余额方向错误，已跳过', 'yellow')
                    continue
            calculate_rule = info_rule['calculate']
            account = 0
            for sign, calculate_num, col in calculate_rule:
                try:
                    account += source_data[calculate_num][col]['account'] * (-1 if sign == '-' else 1)
                except KeyError as e:
                    self.rel_tip_signal.emit(f'计算 {self.names[i]} 时，科目 {calculate_num} 余额不存在，已跳过', 'yellow')
                    break
                except:
                    self.show_msg_signal.emit({'message': f'计算时出现未知错误，科目编号：{calculate_num}，列名：{col}'})
                    self.finished_signal.emit(0)
                    return

            # 检查结果正负
            if account < 0 and info_rule['result_negative'] == 'False':
                self.rel_tip_signal.emit(f'科目 {num} 结果为负，已跳过', 'yellow')
                continue
            if self.unit == '元':
                results[f'C{info_rule["location"]}'] = api_convert.convert_to_wan(account, divisor=1)     # 元，季报
            else:
                results[f'C{info_rule["location"]}'] = api_convert.convert_to_wan(account, decimal_places=1)     # 万元，月报

        if self.unit == '元':
            other_rec_pay = self.other_receivables_payables(source_data, d=1)     # 元，季报
        else:
            other_rec_pay = self.other_receivables_payables(source_data)     # 万元，月报
        results.update(other_rec_pay)
        for key,value in results.items():
            ws_statement[key] = value
                    
        wb_statement.save(self.statement_path)    

        self.finished_signal.emit(1)

class CorrectThread(QThread):
    finished_signal = Signal(int)       # 正常结束发1，提前结束发0
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)

    def __init__(self, right_path, wrong_path):
        super().__init__()
        self.right_path = right_path
        self.wrong_path = wrong_path
    
    def run(self):
        ws_right = load_workbook(self.right_path).active
        right_data = {}
        names = {}
        for the_cell in ws_right['K'][4:]:          # 正确数据取自 K 列，从第五行开始读取
            # key = the_cell.coordinate.replace('K', 'C')     # 因为错误数据是C列，所以单元格位置换成C列
            key = the_cell.coordinate
            right_data[key] = the_cell.value
            names[key] = ws_right[f'A{the_cell.row}'].value
        wb_wrong = load_workbook(self.wrong_path)
        ws_wrong = wb_wrong.active
        # # 删除 D5:K171 的数据
        # for row in ws_wrong.iter_rows(min_row=5, max_row=171, min_col=4, max_col=11):  # 列D=4，列K=11
        #     for cell in row:
        #         cell.value = None  # 清空单元格内容
        wrong_data = {the_cell.coordinate: the_cell.value for the_cell in ws_wrong['K']}
        for key, right_value in right_data.items():
            wrong_value = wrong_data[key]
            if right_value == None:
                right_value = 0
            elif right_value == '——':
                continue
            if wrong_value == None:
                wrong_value = 0
            elif wrong_value == '——':
                continue
            if wrong_value != right_value:
                self.rel_tip_signal.emit(f'{key} ({names[key]}) 值需修改', 'dark')
                ws_wrong[key].offset(column=2).value = right_value - wrong_value
        try:
            wb_wrong.save(self.wrong_path)
        except PermissionError:
            wrong_file = Path(self.wrong_path).stem
            self.show_msg_signal.emit({'message': f'文件 {wrong_file} 正在打开，无法写入，请关闭文件后重新执行'})
            self.finished_signal.emit(0)
            return
        self.finished_signal.emit(1)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = StatementUi()
    ui.show()
    sys.exit(app.exec())


